package com.wzapp.sitposture.usercenter.endpoint;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wzapp.sitposture.authentication.service.AuthenticationService;
import com.wzapp.sitposture.authentication.service.CustomizedDefaultTokenServices;
import com.wzapp.sitposture.common.annotation.NeedLogin;
import com.wzapp.sitposture.common.base.BaseEndpoint;
import com.wzapp.sitposture.common.base.DeviceInfo;
import com.wzapp.sitposture.common.base.HeaderParam;
import com.wzapp.sitposture.common.error.CustomizedException;
import com.wzapp.sitposture.common.exception.AuthenticationException;
import com.wzapp.sitposture.common.page.CommonPage;
import com.wzapp.sitposture.common.page.PageParam;
import com.wzapp.sitposture.common.restful.RestfulResult;
import com.wzapp.sitposture.common.support.HttpKit;
import com.wzapp.sitposture.common.util.PhoneFormatCheckUtils;
import com.wzapp.sitposture.common.util.ToolUtil;
import com.wzapp.sitposture.config.redis.service.RedisService;
import com.wzapp.sitposture.event.BusinessEvent;
import com.wzapp.sitposture.usercenter.entity.*;
import com.wzapp.sitposture.usercenter.param.SettingPostureListParam;
import com.wzapp.sitposture.usercenter.param.UserIdListParam;
import com.wzapp.sitposture.usercenter.service.UserService;
import com.wzapp.sitposture.usercenter.service.UserSettingPostureService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.*;

/**
 * Created by qianglong on 2018/1/16.
 */
@Slf4j
@RequestMapping
@RestController
public class UserCenterEndpoint extends BaseEndpoint {

    @Autowired
    ApplicationContext applicationContext;

    @Autowired
    TokenEndpoint tokenEndpoint;

    @Autowired
    UserService userService;

    @Autowired
    AuthenticationService authenticationService;

    @Autowired
    UserSettingPostureService userSettingPostureService;

    @Autowired
    CustomizedDefaultTokenServices customizedDefaultTokenServices;

    @Autowired
    RedisService redisService;

    @Value("${spring.profiles.active}")
    private String profile;

    /**
     * 功能: 获取学习配置数据<br/>
     *
     * @author qianglong
     * @create 2019/1/9 21:59
     * @param id
     * @return UserConfig
     **/
    @GetMapping("/userCenter/userConfigs/{id}")
    public UserConfig findUserConfigById(@PathVariable String id){
        
        return userService.findUserConfigById(id);
    }


    /**
     * 功能: 修改学习配置数据<br/>
     *
     * @author qianglong
     * @create 2019/1/9 22:01
     * @param id
     * @param userConfig
     * @return UserConfig
     **/
    @PatchMapping("/userCenter/userConfigs/{id}")
    public UserConfig updateUserConfigById(@PathVariable String id,@RequestBody UserConfig userConfig) throws CustomizedException{
        if(ToolUtil.isEmpty(userConfig)){
            throw new CustomizedException("public.param.blank","userConfig");
        }
        userConfig.setId(id);
        return userService.updateUserConfig(userConfig);
    }

    /**
     * 手机号码注册<br/>
     *
     * @param user 注册信息<br/>
     * @param code 验证码<br/>
     * @return
     */
    @PostMapping("/userCenter/users/action/register")
    public ResponseEntity register(@RequestBody User user, @RequestParam(name = "code") String code) throws CustomizedException, IOException {

        String mobile = user.getMobile();
        String password = user.getPassword();

        if (ToolUtil.isOneEmpty(mobile, password)) {
            throw new CustomizedException("userCenter.mobile.password.is.null");
        }
        if (!PhoneFormatCheckUtils.isFixNumbers(mobile)) {
            throw new CustomizedException("userCenter.mobile.format.error");
        }
        if (!authenticationService.verifyCode(mobile, code)) {
            throw new CustomizedException("userCenter.code.is.error");
        }
        AuthParam authParam = getAuthParam(mobile, password);

        HttpKit.getRequest().setAttribute("loginType", "mobile");

        User userDB = userService.register(user);

        AccessToken accessToken = getAccessToken(authParam);
        userDB.setAccessToken(accessToken);

        this.publishLoginLogEvent(userDB);

        //验证成功后，移除验证码
        authenticationService.removeRedisCode(mobile);
        return RestfulResult.createSuccess(userDB);

    }

    private AuthParam getAuthParam(String account, String password) throws CustomizedException {

        HeaderParam headerParam = getHeaderParam();
        String clientInfo = headerParam.getClientInfo();
        if (StringUtils.isEmpty(clientInfo)) {
            throw new CustomizedException("public.error", "clientInfo不能为空");
        }
        if (!clientInfo.startsWith("Basic ")) {
            throw new CustomizedException("public.error", "clientInfo参数错误" + clientInfo);
        }

        clientInfo = clientInfo.substring("Basic ".length());

        AuthParam authParam = new AuthParam();
        String base64 = new String(Base64.decodeBase64(clientInfo));
        String[] base64Arr = base64.split(":");
        if (base64Arr.length != 2) {
            throw new CustomizedException("public.error", "clientInfo参数错误：" + clientInfo);
        }
        authParam.setGrantType("password");
        authParam.setUsername(account);
        authParam.setPassword(password);
        authParam.setClientId(base64Arr[0]);
        authParam.setClientSecret(base64Arr[1]);
        return authParam;
    }

    /**
     * 用户登录<br/>
     *
     * @param user 登录信息<br/>
     * @return
     */
    @PatchMapping("/userCenter/users/action/login")
    @Deprecated
    public ResponseEntity login(@RequestBody User user) throws CustomizedException {

        //todo 弃用 提供新的接口返回用户信息
        //登录账号可能是用户手机号码或账号名称
        String mobile = user.getMobile();
        String password = user.getPassword();

        if (ToolUtil.isOneEmpty(mobile, password)) {
            throw new CustomizedException("userCenter.mobile.password.is.null");
        }
//        if (!PhoneFormatCheckUtils.isFixNumbers(mobile)) {
//            throw new CustomizedException("userCenter.mobile.format.error");
//        }

        HeaderParam headerParam = super.getHeaderParam();
        String signOs = headerParam.getOs();

        Map map = userService.loginByAccount(mobile, password,signOs);
        User userDB = (User) map.get("user");
        String loginType = (String) map.get("loginType");

        //如果登录手机号为空则直接返回用户信息，前端判断没有手机号进行下一步绑定手机号操作完成方可登录成功
        if (StringUtils.isEmpty(userDB.getMobile())){
            return  RestfulResult.createSuccess(userDB);
        }
        AuthParam authParam = getAuthParam(mobile, password);
        HttpKit.getRequest().setAttribute("loginType", loginType);

        AccessToken accessToken = getAccessToken(authParam);
        userDB.setAccessToken(accessToken);

        this.publishLoginLogEvent(userDB);

        return RestfulResult.createSuccess(userDB);
    }

    /**
     * 举报<br/>
     *
     * @param userIdListParam 登录信息<br/>
     * @return
     */
    @PostMapping("/userCenter/users/action/reportAbuse")
    public ResponseEntity report(@RequestBody UserIdListParam userIdListParam) throws CustomizedException {

        log.info(userIdListParam.toString());

        JSONObject result = new JSONObject();
        result.put("status", "OK");
        log.info("hahhahh==========================================");
        throw new CustomizedException("public.api.notsupport");
    }

    /**
     * 手机找回密码<br/>
     *
     * @param user 用户信息<br/>
     * @param code 验证码<br/>
     * @return
     */
    @PatchMapping("/userCenter/users/action/resetPassword")
    public ResponseEntity resetPassword(@RequestBody User user, @RequestParam(name = "code") String code) throws CustomizedException {

        String mobile = user.getMobile();
        String password = user.getPassword();

        if (ToolUtil.isOneEmpty(mobile, password)) {
            throw new CustomizedException("userCenter.mobile.password.is.null");
        }
        if (!PhoneFormatCheckUtils.isFixNumbers(mobile)) {
            throw new CustomizedException("userCenter.mobile.format.error");
        }
        if (!authenticationService.verifyCode(mobile, code)) {
            throw new CustomizedException("userCenter.code.is.error");
        }

        User userDB = userService.getUserByMobile(mobile);
        if (ToolUtil.isEmpty(userDB)) {
            throw new AuthenticationException("userCenter.user.not.exists.error");
        }

        AuthParam authParam = getAuthParam(mobile, password);
        HttpKit.getRequest().setAttribute("loginType", "mobile");

        userDB = userService.resetPassword(userDB, password);

        AccessToken accessToken = getAccessToken(authParam);
        userDB.setAccessToken(accessToken);

        this.publishLoginLogEvent(userDB);

        return RestfulResult.createSuccess(userDB);
    }


    /**
     * 查询用户信息<br/>
     * 新的登录后只提供token,用户信息需要从该接口获取
     *
     * @param id 用户id<br/>
     * @return
     */
    @GetMapping("/userCenter/users/{id}")
    public User getUserById(@PathVariable String id) throws CustomizedException {

        log.debug("根据userId查询user，userId：{}", id);
        User user = userService.getFullUserById(id);

        if (ToolUtil.isEmpty(user)) {
            throw new CustomizedException("userCenter.user.not.exists.error");
        }
        user.setToken("");

        //补充关注信息
        HeaderParam headerParam = getHeaderParam();
        String userId = headerParam.getUserId();
        //隐私保护，非自己查询，不返回手机号码
        if (userId == null || !id.equals(userId)) {
            user.setMobile("");
        }
        return user;
    }

    /**
     * 修改用户信息，包括个人简介<br/>
     *
     * @return
     */
    @NeedLogin
    @PatchMapping("/userCenter/users/{id}")
    @CacheEvict(value = "getSimpleUserById", key = "#id")
    public ResponseEntity updateUser(@RequestBody User user, @PathVariable String id) throws CustomizedException {

        if (ToolUtil.isEmpty(user)) {
            throw new CustomizedException("userCenter.user.invalid.request.error", "user");
        }

        HeaderParam headerParam = getHeaderParam();
        String userId = headerParam.getUserId();
        if (!id.equals(userId)) {
            throw new AuthenticationException("userCenter.user.token.error");
        }

        user.setId(id);
        User userDB = userService.updateUser(user, headerParam);
        return RestfulResult.createSuccess(userDB);
    }

    /**
     * 通过邀请码查询用户信息<br/>
     *
     * @return
     */
    @GetMapping("/userCenter/users/findUserByRecommendCode")
    public ResponseEntity findUserByRecommendCode(String recommendCode) {

        if (ToolUtil.isEmpty(recommendCode)) {
            return null;
        }
        User userDB = userService.findUserByRecommendCode(recommendCode);
        return RestfulResult.createSuccess(userDB);
    }

    /**
     * 功能: 获取用户收益列表<br/>
     *
     * @author qianglong
     * @create 2019/1/12 19:44
     * @param pageParam
     * @return org.springframework.data.domain.Page
     **/
    @GetMapping("/userCenter/userIncomes")
    public Page findIncomeList(PageParam pageParam){
        String userId = super.getUserIdFromToken();
        if (ToolUtil.isEmpty(userId)) {
            throw new AuthenticationException("userCenter.user.token.error");
        }
        return userService.findIncomeByUserId(userId,pageParam);
    }


    /**
     * 功能: 批量上传用户坐姿信息<br/>
     *
     * @author qianglong
     * @create 2019/1/27 16:33
     * @param listParam
     * @return org.springframework.http.ResponseEntity
     **/
    @NeedLogin
    @PostMapping("/userCenter/settingPostures/action/batchSave")
    public ResponseEntity saveSettingPosture(@RequestBody SettingPostureListParam listParam)throws
            CustomizedException{

        if(ToolUtil.isEmpty(listParam)){
            return RestfulResult.createSuccess(null);
        }

        String userId = super.getUserIdFromToken();
        List<UserSettingPosture> settingPostures = listParam.getSettingPostures();
        for (UserSettingPosture settingPosture : settingPostures){
            settingPosture.setUserId(userId);
            settingPosture.setCreateTime(System.currentTimeMillis());
        }

        userSettingPostureService.batchSave(settingPostures);

        return RestfulResult.createSuccess(null);
    }

    /**
     * 功能: 上传用户坐姿信息<br/>
     *
     * @author qianglong
     * @create 2019/1/27 16:33
     * @param settingPosture
     * @return org.springframework.http.ResponseEntity
     **/
    @NeedLogin
    @PostMapping("/userCenter/settingPostures")
    public ResponseEntity saveSettingPosture(@RequestBody UserSettingPosture settingPosture)throws
            CustomizedException{

        if(ToolUtil.isEmpty(settingPosture.getUrl())){
            throw new CustomizedException("public.param.blank","url");
        }
        String userId = super.getUserIdFromToken();
        settingPosture.setUserId(userId);

        return RestfulResult.createSuccess(userSettingPostureService.save(settingPosture));
    }

    /**
     * 功能: 用户-家长获取近半个小时学习姿态<br/>
     *
     * @author qianglong
     * @create 2019/1/27 16:51
     * @param startTime
     * @return com.wzapp.sitposture.common.page.CommonPage
     **/
    @NeedLogin
    @GetMapping("/userCenter/settingPostures/search/findByStartTime")
    public CommonPage<UserSettingPosture> findByStartTime(Long startTime, PageParam pageParam){
        String userId = super.getUserIdFromToken();
        return userSettingPostureService.findByStartTime(userId,startTime,pageParam);
    }

    /**
     * 功能: 获取某天学习统计数据<br/>
     *
     * @author qianglong
     * @create 2019/1/27 17:13
     * @param statisticDate
     * @return com.wzapp.sitposture.common.page.CommonPage
     **/
    @NeedLogin
    @GetMapping("/userCenter/userStatistics/search/findByStatisticDate")
    public Map findByStatisticDate(Long statisticDate){
        String userId = super.getUserIdFromToken();
        return userSettingPostureService.findByStatisticDate(userId,statisticDate);
    }

    private AccessToken getAccessToken(AuthParam authParam) {

        String tokenKey = "APP_USER_LIMIT_" + authParam.getUsername();
        int limitCount = this.getLoginTimes(tokenKey);

        //限制登录，只能2个登录设备
        if(limitCount >=2 ){
            //删除老的登录token
            User user = userService.getUserByMobile(authParam.getUsername());
            customizedDefaultTokenServices.revokeTokenByUserId("oauth_android",user.getId()+"");
            customizedDefaultTokenServices.revokeTokenByUserId("oauth_ios",user.getId()+"");
            redisService.set(tokenKey,1);
        }else {
            redisService.set(tokenKey,limitCount + 1);
        }

        //生成用户token 返回 访问oauth 生成token
        Map<String, String> parameters = new HashMap<>(4);
        parameters.put("grant_type", "password");
        parameters.put("username", authParam.getUsername());
        parameters.put("password", authParam.getPassword());
        parameters.put("access_token", HttpKit.getAccessToken());
        ResponseEntity<OAuth2AccessToken> tokenResponseEntity = null;
        Set<GrantedAuthority> userAuthotities = new HashSet<>();
        org.springframework.security.core.userdetails.User user = new org.springframework.security.core.userdetails.User(authParam.getClientId(), authParam.getClientSecret(), userAuthotities);
        try {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken
                    (user, null, userAuthotities);
            tokenResponseEntity = tokenEndpoint.postAccessToken(authenticationToken, parameters);
        } catch (HttpRequestMethodNotSupportedException e) {
            log.error(e.getMessage(), e);
        }
        AccessToken token = getOAuthToken(tokenResponseEntity);

        return token;
    }

    private int getLoginTimes(String tokenKey){

        Object limitObject = redisService.get(tokenKey);
        int limitCount =0;
        if(ToolUtil.isNotEmpty(limitObject)){
            limitCount = Integer.parseInt(limitObject.toString());
        }

        return limitCount;
    }

    /**
     * 功能: 通过okhttp 访问oauth接口，或者调用生成oauthservice<br/>
     *
     * @param
     * @return com.alibaba.fastjson.JSONObject
     * @author ql
     * @create 2018-06-14 20:00
     **/
    private AccessToken getOAuthToken(ResponseEntity<OAuth2AccessToken> tokenResponseEntity) {

        AccessToken accessToken = new AccessToken();
        if (null != tokenResponseEntity) {
            OAuth2AccessToken oAuth2AccessToken = tokenResponseEntity.getBody();
            accessToken.setAccess_token(oAuth2AccessToken.getValue());
            accessToken.setToken_type(oAuth2AccessToken.getTokenType());
            accessToken.setRefresh_token(oAuth2AccessToken.getRefreshToken().getValue());
            accessToken.setExpires_in(oAuth2AccessToken.getExpiresIn());
            accessToken.setScope(StringUtils.join(oAuth2AccessToken.getScope(), ","));
        }
        return accessToken;
    }

    /**
     * 记录登录日志<br/>
     *
     * @param user 用户
     */
    private void publishLoginLogEvent(User user) throws CustomizedException {

        publishLoginLogEvent(user.getId());
    }

    private void publishLoginLogEvent(String id) throws CustomizedException {

        UserLoginLog userLoginLog = new UserLoginLog();
        userLoginLog.setUserId(id);
        userLoginLog.setCreateTime(System.currentTimeMillis());
        userLoginLog.setUpdateTime(System.currentTimeMillis());
        userLoginLog.setLoginTime(System.currentTimeMillis());

        HeaderParam headerParam = super.getHeaderParam();
        userLoginLog.setRequestIp(headerParam.getRemoteIp());
        userLoginLog.setTerminalType(headerParam.getTerminalType());

        userLoginLog.setClientApplication(headerParam.getClientApplication());

        DeviceInfo deviceInfo = headerParam.getDeviceInfo();
        userLoginLog.setDeviceInfo(JSON.parseObject(JSON.toJSONString(deviceInfo)));

        HashMap<String, Object> param = new HashMap<String, Object>(1);
        param.put("userLoginLog", userLoginLog);
        applicationContext.publishEvent(new BusinessEvent(this, "userLogin", param));
    }
}
